package com.itheima.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.itheima.common.DefaultException;
import com.itheima.common.Paging;
import com.itheima.dto.NewsDto;
import com.itheima.dto.WebsocketDto;
import com.itheima.entity.News;
import com.itheima.entity.User;
import com.itheima.entity.UserLinkUser;
import com.itheima.enums.OrganizationEnum;
import com.itheima.enums.WebsocketEnum;
import com.itheima.mapper.NewsMapper;
import com.itheima.mapper.UserLinkUserMapper;
import com.itheima.service.NewsService;
import com.itheima.service.SensitiveWordFilterService;
import com.itheima.service.UserLinkUserService;
import com.itheima.service.UserService;
import com.itheima.utils.CheckUtil;
import com.itheima.utils.PageUtils;
import com.itheima.utils.Websocket;
import com.itheima.vo.NewsVo;
import com.itheima.vo.WebsocketVo;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 51585
 * @description 针对表【news】的数据库操作Service实现
 * @createDate 2022-10-26 09:55:02
 */
@Service
public class NewsServiceImpl extends ServiceImpl<NewsMapper, News>
        implements NewsService {

    @Resource
    private UserService userService;
    @Resource
    private UserLinkUserService userLinkUserService;
    @Resource
    private NewsService newsService;
    @Resource
    private UserLinkUserMapper userLinkUserMapper;
    @Resource
    private Websocket webSocket;
    @Resource
    private NewsMapper newsMapper;
    @Resource
    private SensitiveWordFilterService sensitiveWordFilterService;

    /**
     * 发送消息
     *
     * @param websocketDto
     * @throws Exception
     */
    public void sendPersonalNews(WebsocketDto websocketDto) throws Exception {
        String userId = websocketDto.getSendId();
        try {
            // 检查权限
            boolean power = checkPower(userId, websocketDto.getAcceptId());
            if (!power) {
                // 查看历史消息3条
                if (!checkHistory(userId, websocketDto.getAcceptId())) {
                    throw new DefaultException("当天发送已达上线，请等待回复");
                }
            }
            filterWebsockDto(websocketDto);
            // 发送消息
            Integer newsId = doSend(userId, websocketDto);
            WebsocketVo websocketVo = convertWebsocketDtoToWebsocketVo(websocketDto, userId, newsId);
            webSocket.sendMoreMessage(websocketVo);
        } catch (Exception e) {
            WebsocketVo websocketVo = new WebsocketVo();
            websocketVo.setSendUserId(userId);
            websocketVo.setType(WebsocketEnum.EXCEPTION.name());
            websocketVo.setException(e.getMessage());
            webSocket.sendOneMessage(websocketVo);
        }

    }

    /**
     * webSocketDto  ->   websocketVo
     *
     * @param websocketDto
     * @return
     */
    public WebsocketVo convertWebsocketDtoToWebsocketVo(WebsocketDto websocketDto, String sendId, Integer newsId) {
        WebsocketVo websocketVo = new WebsocketVo();
        User user = userService.getById(sendId);
        websocketVo.setId(newsId);
        websocketVo.setSendUserId(sendId);
        websocketVo.setSendUserName(user.getName());
        websocketVo.setSendTime(LocalDateTime.now());
        websocketVo.setSendUserImageUrl(user.getImageUrl());
        websocketVo.setContent(websocketDto.getContent());
        List<String> acceptIds = new ArrayList<>(2);
        acceptIds.add(websocketDto.getAcceptId());
        acceptIds.add(sendId);
        acceptIds = acceptIds.stream().distinct().collect(Collectors.toList());
        websocketVo.setAcceptIds(acceptIds);
        websocketVo.setType(WebsocketEnum.NEWS.name());
        return websocketVo;
    }

    @Override
    public List<NewsVo> showRecentNews(String bUserId) throws Exception {
        String userId = CheckUtil.checkLogin();
        List<NewsVo> newsList = newsMapper.showRecentNews(userId, bUserId);
        Collections.reverse(newsList);
        supplyNewsUserMessage(newsList, userId, bUserId);
        return newsList;
    }

    @Override
    public Paging<NewsVo> pageNews(NewsDto newsDto) throws Exception {
        String bUserId = CheckUtil.checkLogin();
//        String bUserId = "19251104253";
        Page<?> page = new Page<>(newsDto.getPageIndex(), newsDto.getPageSize());
        IPage<NewsVo> iPage = newsMapper.pageNews(page, newsDto, bUserId);
        supplyNewsUserMessage(iPage.getRecords(), bUserId, newsDto.getUserId());
        return PageUtils.toPage(iPage);
    }

    @Override
    public void updateReadStatus(String bUserId) throws Exception {
        String userId = CheckUtil.checkLogin();
//        String userId = "19251104252";
        boolean update = this.lambdaUpdate()
                .set(News::getReadStatus, 1)
                .eq(News::getSendId, bUserId)
                .eq(News::getAcceptId, userId)
                .isNull(News::getDeletedAt)
                .update();
    }

    @Override
    public void deleteNews(Integer newsId) throws Exception {
        String userId = CheckUtil.checkLogin();
//        String userId = "19251104252";
        final Long minute = 2L;
        News news = this.lambdaQuery()
                .select(News::getSendId, News::getSendTime)
                .eq(News::getId, newsId)
                .one();
        if (StringUtils.isEmpty(news)) {
            throw new DefaultException("当前消息不存在");
        }
        if (!userId.equals(news.getSendId())) {
            throw new DefaultException("没有权限");
        }
        long between = DateUtil.between(DateUtil.parse(news.getSendTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                , DateUtil.parse(DateUtil.now())
                , DateUnit.MINUTE);
        if (between > minute) {
            throw new DefaultException("发送时间超过2分钟，无法撤回");
        }
        boolean update = this.lambdaUpdate()
                .set(News::getDeletedAt, LocalDateTime.now())
                .eq(News::getId, newsId)
                .update();
        if (!update) {
            throw new DefaultException("撤回失败");
        }
    }

    @Override
    public List<WebsocketVo> offlineNews(String userId) {
        List<News> newsList = this.lambdaQuery()
                .eq(News::getAcceptId, userId)
                .eq(News::getReadStatus, 0)
                .orderByAsc(News::getSendTime)
                .list();
        if (CollectionUtils.isEmpty(newsList)) {
            return null;
        }
        List<String> collect = newsList.stream()
                .map(News::getSendId).distinct()
                .collect(Collectors.toList());
        List<User> list = userService.lambdaQuery()
                .in(User::getId, collect)
                .list();
        Map<String, List<String>> collect1 = list.stream()
                .collect(Collectors.groupingBy(User::getId, Collectors.mapping(User::getName, Collectors.toList())));

        List<WebsocketVo> res = newsList.stream().map(news -> {
            WebsocketVo websocketVo = new WebsocketVo();
            List<String> acceptIds = new ArrayList<>(1);
            acceptIds.add(userId);
            websocketVo.setAcceptIds(acceptIds);
            User one = userService.lambdaQuery()
                    .select(User::getName, User::getImageUrl)
                    .eq(User::getId, news.getSendId())
                    .one();
            Optional.ofNullable(one).ifPresent(k -> {
                websocketVo.setSendUserName(k.getName());
                websocketVo.setSendUserImageUrl(k.getImageUrl());
            });
            websocketVo.setId(news.getId());
            websocketVo.setSendUserId(news.getSendId());
            websocketVo.setSendTime(news.getSendTime());
            websocketVo.setContent(news.getContent());
            websocketVo.setType(WebsocketEnum.NEWS.name());
            websocketVo.setSendUserName(collect1.get(news.getSendId()).get(0));

            return websocketVo;
        }).collect(Collectors.toList());
        return res;
    }

    /**
     * 补充消息用户信息
     *
     * @param newsList 消息vo
     * @param userId   用户id
     * @param bUserId  好友id
     */
    public void supplyNewsUserMessage(List<NewsVo> newsList, String userId, String bUserId) {
        List<User> userList = userService.lambdaQuery()
                .select(User::getId, User::getImageUrl, User::getName)
                .eq(User::getId, bUserId).or().eq(User::getId, userId)
                .list();
        Map<String, User> map = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
        newsList.forEach(k -> {
            k.setSendUserName(map.get(k.getSendUserId()).getName());
            k.setSendUserImageUrl(map.get(k.getSendUserId()).getImageUrl());

            k.setAcceptUserName(map.get(k.getAcceptUserId()).getName());
            k.setAcceptUserImageUrl(map.get(k.getAcceptUserId()).getImageUrl());
        });
    }

    /**
     * 检查权限
     *
     * @param sendId
     * @param acceptId
     * @return
     * @throws Exception
     */
    private boolean checkPower(String sendId, String acceptId) throws Exception {
        User sendUser = userService.getById(sendId);
        User acceptUser = userService.getById(acceptId);
        if (ObjectUtils.isEmpty(sendUser) || ObjectUtils.isEmpty(acceptUser)) {
            throw new DefaultException("发送用户或接收用户不存在");
        }
        if (!sendUser.getOrganizationId().equals(OrganizationEnum.school.getOrganizationId())
                && !sendUser.getOrganizationId().equals(OrganizationEnum.administrators.getOrganizationId())
                && !sendUser.getOrganizationId().equals(acceptUser.getOrganizationId())) {
            return CheckUtil.checkRole(sendUser.getOrganizationId(), acceptUser.getOrganizationId());
        }
        return true;
    }

    /**
     * 发送消息
     *
     * @param userId
     * @param websocketDto
     * @return 消息id
     */
    public Integer doSend(String userId, WebsocketDto websocketDto) throws DefaultException {
        User user = userService.getById(websocketDto.getAcceptId());
        if (StringUtils.isEmpty(user)) {
            throw new DefaultException("选择的用户不存在");
        }
        UserLinkUser one = userLinkUserService.lambdaQuery()
                .select()
                .eq(UserLinkUser::getUserAid, userId)
                .eq(UserLinkUser::getUserBid, websocketDto.getAcceptId())
                .one();
        if (Objects.isNull(one)) {
            // 互相存储到聊天列表
            List<UserLinkUser> list = new ArrayList<>(2);
            UserLinkUser userLinkUser1 = new UserLinkUser();
            userLinkUser1.setUserAid(userId);
            userLinkUser1.setUserBid(websocketDto.getAcceptId());

            UserLinkUser userLinkUser2 = new UserLinkUser();
            userLinkUser2.setUserAid(websocketDto.getAcceptId());
            userLinkUser2.setUserBid(userId);
            list.add(userLinkUser1);
            list.add(userLinkUser2);
            List<UserLinkUser> collect = list.stream().distinct().collect(Collectors.toList());
            userLinkUserService.saveBatch(collect);
        }
        News news = new News();
        news.setSendId(userId);
        news.setAcceptId(websocketDto.getAcceptId());
        news.setContent(websocketDto.getContent());
        news.setSendTime(LocalDateTime.now());
        newsService.save(news);
        return news.getId();
    }

    /**
     * 判断发送消息是否超过限定（低级向高级）
     *
     * @param sendUserId
     * @param acceptUserId
     * @return
     */
    public boolean checkHistory(String sendUserId, String acceptUserId) {
        int number = 3;
        int day = 1;
        List<LocalDateTime> history = newsMapper.checkHistory(sendUserId, acceptUserId);
        if (history.size() < number) {
            return true;
        }
        //获取2个日期的间隔（以天为单位）
        long between = DateUtil.between(DateUtil.parse(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                , DateUtil.parse(String.valueOf(history.get(number - 1)))
                , DateUnit.DAY);
        if (between >= day) {
            return true;
        }
        List<LocalDateTime> chatList = newsMapper.checkHistory(acceptUserId, sendUserId);
        if (!CollectionUtils.isEmpty(chatList)) {
            if (chatList.get(0).isAfter(history.get(number - 1))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 过滤WebsocketDto中的敏感信息
     *
     * @param websocketDto
     */
    public void filterWebsockDto(WebsocketDto websocketDto) throws JsonProcessingException {
        websocketDto.setContent(sensitiveWordFilterService.filter(websocketDto.getContent()));
    }

}




